#ifndef __M__EXCHANGE_H_
#define __M__EXCHANGE_H_

#include "../mqCommon/Helper.hpp"
#include "../mqCommon/Logger.hpp"
#include "../mqCommon/proto/mq_message.pb.h"
#include <iostream>
#include <unordered_map>
#include <memory>
#include <mutex>

using namespace mq_message;
using namespace mq;
using namespace std;

namespace mq
{
    struct Exchange;
    using ex_ptr = shared_ptr<Exchange>;
    using ExchangeMap = unordered_map<string, ex_ptr>;
    using Map = google::protobuf::Map<string, string>;
    // 1.交换机类
    struct Exchange
    {
        using ptr = shared_ptr<Exchange>;
        string _name;       // 交换机名称
        ExchangeType _type; // 类型
        bool _durable;      // 持久化标志
        bool _auto_delete;  // 自动删除标志
        Map _args;          // 其它参数

        Exchange(const string &name, ExchangeType type,
                 bool durable, bool auto_delete,
                 Map &args)
            : _name(name), _type(type), _durable(durable), _auto_delete(auto_delete), _args(args) {}
        Exchange() {}

        // 数据库存储以格式化字符串key=val&key=val...形式存储
        void setArgs(const string &str_args) // 传入格式化字符串k=v&k=v...，将其中键值对提取到args中
        {
            vector<string> args_list;
            StrHelper::split(str_args, "&", args_list); // key=val
            for (auto &str : args_list)
            {
                size_t pos = str.find("=");
                string key = str.substr(0, pos);
                string val = str.substr(pos + 1);
                _args[key] = val;
            }
        }
        string getArgs() // 将args中键值对提取，格式化为字符串k=v&k=v...
        {
            string ret;
            auto it = _args.begin();
            while (it != _args.end())
            {
                ret += it->first + "=" + it->second + "&";
                it++;
            }
            if (!ret.empty())
                ret.pop_back(); // 去掉最后一个&
            return ret;
        }
    };

    // 2.交换机数据持久化管理类--数据存储在sqlite中
    class ExchangeMapper
    {
    public:
        ExchangeMapper(const string &dbfile) : _sql_helper(dbfile)
        {
            // 创建文件前先创建目标路径
            string path = FileHelper::parentDirectory(dbfile);
            bool ret1 = FileHelper::createDirectory(path);
            if (!ret1)
            {
                ELOG("创建数据库文件路径时出错：%s", path);
                abort();
            }

            bool ret2 = _sql_helper.open(); // 打开/创建数据库文件
            if (!ret2)
            {
                ELOG("打开数据库文件失败：%s", dbfile);
                abort();
            }
            createTable();
        }
        void createTable() // 创建交换机数据表
        {
#define CREATE_TABLE "create table if not exists exchange_table(\
                                name varchar(32) primary key, \
                                type int, durable int, auto_delete int, \
                                args varchar(128));"
            bool ret = _sql_helper.exec(CREATE_TABLE, NULL, NULL);
            if (!ret)
            {
                ELOG("创建交换机数据库表失败");
                abort();
            }
        }
        void removeTable() // 删除交换机数据表
        {
#define DROP_TABLE "drop table if exists exchange_table;"
            bool ret = _sql_helper.exec(DROP_TABLE, NULL, NULL);
            if (!ret)
            {
                ELOG("删除交换机数据库表失败");
                abort();
            }
        }
        bool insert(ex_ptr &exchange) // 新增交换机
        {
#define INSERT_SQL "insert into exchange_table values('%s',%d,%d,%d,'%s');"
            char sql_str[4096];
            string str_args = exchange->getArgs();
            snprintf(sql_str, sizeof(sql_str) - 1, INSERT_SQL, exchange->_name.c_str(), exchange->_type, exchange->_durable, exchange->_auto_delete, str_args.c_str());
            bool ret = _sql_helper.exec(sql_str, NULL, NULL);
            if (!ret)
            {
                ELOG("新增交换机失败：%s", exchange->_name.c_str());
                return false;
            }
            return true;
        }
        bool remove(const string &name) // 删除交换机
        {
            std::stringstream ss;
            ss << "delete from exchange_table where name='" << name << "';";
            bool ret = _sql_helper.exec(ss.str(), NULL, NULL);
            if (!ret)
            {
                ELOG("删除交换机失败：%s", name.c_str());
                return false;
            }
            return true;
        }
        // ex_ptr getOne(const string &name);      // 获取一台交换机
        ExchangeMap recovery() // 获取交换机数据库表中所有交换机：恢复数据
        {
            string select_sql = "select name, type, durable, auto_delete, args from exchange_table;"; // 查询所有交换机信息
            ExchangeMap _exchanges;
            bool ret = _sql_helper.exec(select_sql, selectCallback, (void *)&_exchanges);
            if (!ret)
            {
                ELOG("恢复交换机数据过程出错");
            }
            return _exchanges;
        }

    private:
        static int selectCallback(void *args, int col_num, char **row, char **fields) // 查询回调，每一行都调用该回调
        {
            ExchangeMap *result = (ExchangeMap *)args;
            auto exp = make_shared<Exchange>(); // 创建一个交换机对象
            exp->_name = row[0];
            exp->_type = (mq_message::ExchangeType)stoi(row[1]);
            exp->_durable = (bool)stoi(row[2]);
            exp->_auto_delete = (bool)stoi(row[3]);
            if (row[4])
                exp->setArgs(row[4]);
            result->insert(make_pair(exp->_name, exp));
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    // 3.交换机数据内存管理类
    class ExchangeManager
    {
    public:
        using manager_ptr = shared_ptr<ExchangeManager>;
        ExchangeManager(const string &dbfile) : _mapper(dbfile)
        {
            _exchanges = _mapper.recovery(); // 恢复历史数据
        }
        bool declareExchange(const string &name, ExchangeType type,
                             bool durable, bool auto_delete,
                             Map &args) // 声明交换机
        {
            unique_lock<mutex> lock(mtx); // 多线程下
            if (exists(name))             // 若已存在，不需要重复添加
                return true;
            auto exp = make_shared<Exchange>(name, type, durable, auto_delete, args); // 创建一个交换机对象

            if (exp->_durable) // 该交换机需要持久化，才加入mapper中
            {
                bool ret = _mapper.insert(exp);
                if (!ret)
                {
                    DLOG("交换机持久化失败：%s", name.c_str());
                    return false;
                }
            }
            _exchanges.insert(make_pair(exp->_name, exp));
            return true;
        }
        void deleteExchange(const string &name) // 删除交换机
        {
            unique_lock<mutex> lock(mtx); // 多线程下
            if (!exists(name))            // 不存在，直接返回
                return;
            ex_ptr exp = _exchanges[name];
            if (exp->_durable)
                _mapper.remove(name);
            _exchanges.erase(name);
        }
        ex_ptr selectExchange(const string &name) // 获取指定交换机
        {
            unique_lock<mutex> lock(mtx); // 多线程下
            if (!exists(name))            // 不存在，直接返回空
                return ex_ptr();
            return _exchanges[name];
        }
        bool exists(const string &name) // 判断交换机是否存在:需要加锁
        {
            auto it = _exchanges.find(name);
            return it != _exchanges.end();
        }
        void clear() // 销毁所有数据
        {
            unique_lock<mutex> lock(mtx);
            _mapper.removeTable();
            _exchanges.clear();
        }
        size_t size() // 返回交换机数量
        {
            unique_lock<mutex> lock(mtx);
            return _exchanges.size();
        }

    private:
        mutex mtx;              // 加锁保护
        ExchangeMapper _mapper; // 持久化管理类
        ExchangeMap _exchanges; // 交换机map
    };

}

#endif